Skip to content

feat(android): add unstable_Immediate UpdateMode for synchronous state updates#57164

Open
intergalacticspacehighway wants to merge 2 commits into
react:mainfrom
intergalacticspacehighway:android/sync-update-mode
Open

feat(android): add unstable_Immediate UpdateMode for synchronous state updates#57164
intergalacticspacehighway wants to merge 2 commits into
react:mainfrom
intergalacticspacehighway:android/sync-update-mode

Conversation

@intergalacticspacehighway

@intergalacticspacehighway intergalacticspacehighway commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary:

Currently we support unstable_Immediate mode on updateState for iOS. This allows us to dispatch synchronous shadow node size updates in Expo UI SwiftUI (communicating SwiftUI children size to Yoga. Host for more details).

Since it is not enabled on android, it leads to layout shift when mixing Compose and React Native components as Compose tree currently dispatches size information asynchronously to the shadow node.

Here, the video demonstrates the asynchronous vs synchronous updateState usage (before and after applying the changes in this PR). Repro link

Screen.Recording.2026-04-02.at.8.52.27.PM.mov

More:

After enabling the update_immediate, I noticed the flicker was still happening. The issue is that update layout instruction triggers a follow up instruction when we set the shadow node size from Expo UI which gets skipped to the next frame. This is handled on iOS using _followUpTransactionRequired flag here. This seems to be added on iOS for similar reasons here when dispatching an instruction can lead to a follow-up instruction when synchronous update is triggered. So we also handle the sync follow up transaction in this PR using similar approach to iOS's.

Changelog:

[ANDROID] [ADDED] - Add unstable_Immediate UpdateMode support in updateState.

Test Plan:

  • Tested the changes in the repro. Also tested RN Tester, it should be a non breaking change.
  • Added unit tests to test the follow up re-entrancy event dispatch.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 11, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jun 11, 2026
intergalacticspacehighway added a commit to expo/expo that referenced this pull request Jul 15, 2026
…47810)

## Why

Fixes - #47778

Compose-hosted RN content (e.g. `@expo/ui` community `BottomSheet`) can
keep a stale size after the keyboard is dismissed, stranding
bottom-anchored content in the middle of the sheet.

## How

- The shadow node size flush is deferred to a one-shot pre-draw listener
(to avoid the RN state-update re-entrancy early return). During a
window-inset animation (keyboard show/hide) the pre-draw listener does
not reliably get called for each frame, it misses the last few frame
sizes which has the updated height.
- Post the pending flush to the view. It drains the latest size on the
next main-loop turn, and no-ops if the pre-draw already drained it.

This is not ideal fix. We need this
react/react-native#57164 for a proper fix.

## Test Plan

Tested the user shared repro.


https://github.com/user-attachments/assets/cc526038-e10b-4349-ba5a-3708f9adfe6a



## Checklist

- [x] Added a changelog entry.
intergalacticspacehighway added a commit to expo/expo that referenced this pull request Jul 15, 2026
…47810)

## Why

Fixes - #47778

Compose-hosted RN content (e.g. `@expo/ui` community `BottomSheet`) can
keep a stale size after the keyboard is dismissed, stranding
bottom-anchored content in the middle of the sheet.

## How

- The shadow node size flush is deferred to a one-shot pre-draw listener
(to avoid the RN state-update re-entrancy early return). During a
window-inset animation (keyboard show/hide) the pre-draw listener does
not reliably get called for each frame, it misses the last few frame
sizes which has the updated height.
- Post the pending flush to the view. It drains the latest size on the
next main-loop turn, and no-ops if the pre-draw already drained it.

This is not ideal fix. We need this
react/react-native#57164 for a proper fix.

## Test Plan

Tested the user shared repro.


https://github.com/user-attachments/assets/cc526038-e10b-4349-ba5a-3708f9adfe6a



## Checklist

- [x] Added a changelog entry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant